﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using AForge.Imaging.Filters;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Drawing.Drawing2D;
using gma.System.Windows;
using FourSteps.Properties;
using System.Threading;

namespace FourSteps
{
    public partial class Main : Form
    {
        [DllImport("user32.dll")]
        static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);

        [Flags]
        public enum MouseEventFlags
        {
            LEFTDOWN = 0x00000002,
            LEFTUP = 0x00000004,
            MIDDLEDOWN = 0x00000020,
            MIDDLEUP = 0x00000040,
            MOVE = 0x00000001,
            ABSOLUTE = 0x00008000,
            RIGHTDOWN = 0x00000008,
            RIGHTUP = 0x00000010
        }

        [DllImport("user32.dll")]
        private static extern int GetWindowLong(IntPtr hWnd, int nIndex);

        [DllImport("user32.dll")]
        private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

        enum FourStepStage
        {
            NOT_INITIATED = 0, //When FourStep has not initiated the scan
            ON_SCAN = 1, //On scan
            ON_ACTION_SELECTION = 2, //On click acition selection
        }
        
        FourStepStage currentStage = FourStepStage.NOT_INITIATED;
        FirstTimeRun firstTimeRunDialog;
        Bitmap screenPoint;
        bool disableClickCapture = false;
        bool onFirstTimeRun = true;
        UserActivityHook actHook;
        Screen primaryScreen;
        Rectangle firstCuadrant, secondCuadrant, thirdCuadrant, fourthCuadrant;
        Size cuadrantSize;
        int autoSweepCuadrant;
        int zoomQuantity = 4;
        int actionToPerform;
        int currentCuadrantPosition;
        int lastBrightedCuadrant, lastBrightedAction;
        BrightnessCorrection filter = new BrightnessCorrection();
        Bitmap dimmedScreen;
        Bitmap cursorImage;
        int zoomIndex;
        int[] zoomCuadrants;

        public Main()
        {
            InitializeComponent();
            cuadrantSize = new Size(Screen.PrimaryScreen.Bounds.Width / 2, Screen.PrimaryScreen.Bounds.Height / 2);
            firstCuadrant = new Rectangle(Screen.PrimaryScreen.Bounds.Left, Screen.PrimaryScreen.Bounds.Top, cuadrantSize.Width, cuadrantSize.Height);
            secondCuadrant = new Rectangle(Screen.PrimaryScreen.Bounds.Left + cuadrantSize.Width, Screen.PrimaryScreen.Bounds.Top, cuadrantSize.Width, cuadrantSize.Height);
            thirdCuadrant = new Rectangle(Screen.PrimaryScreen.Bounds.Left, Screen.PrimaryScreen.Bounds.Top + cuadrantSize.Height, cuadrantSize.Width, cuadrantSize.Height);
            fourthCuadrant = new Rectangle(Screen.PrimaryScreen.Bounds.Left + cuadrantSize.Width, Screen.PrimaryScreen.Bounds.Top + cuadrantSize.Height, cuadrantSize.Width, cuadrantSize.Height);

            typeof(Panel).InvokeMember("DoubleBuffered", BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic, null, splitContainer1.Panel1, new object[] { true });
        }

        void temporalMouseDisabler_Tick(object sender, EventArgs e)
        {
            
        }

        #region Internals
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
            this.screenUpdater = new System.Windows.Forms.Timer(this.components);
            this.afterActionDelay = new System.Windows.Forms.Timer(this.components);
            this.autoSweep = new System.Windows.Forms.Timer(this.components);
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
            this.splitContainer1.SuspendLayout();
            this.SuspendLayout();
            // 
            // splitContainer1
            // 
            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.splitContainer1.IsSplitterFixed = true;
            this.splitContainer1.Location = new System.Drawing.Point(0, 0);
            this.splitContainer1.Name = "splitContainer1";
            this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
            // 
            // splitContainer1.Panel1
            // 
            this.splitContainer1.Panel1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
            this.splitContainer1.Panel1MinSize = 100;
            this.splitContainer1.Panel2Collapsed = true;
            this.splitContainer1.Panel2MinSize = 70;
            this.splitContainer1.Size = new System.Drawing.Size(751, 527);
            this.splitContainer1.SplitterDistance = 100;
            this.splitContainer1.TabIndex = 0;
            // 
            // screenUpdater
            // 
            this.screenUpdater.Interval = 20;
            this.screenUpdater.Tick += new System.EventHandler(this.screenUpdater_Tick);
            // 
            // afterActionDelay
            // 
            this.afterActionDelay.Interval = 500;
            this.afterActionDelay.Tick += new System.EventHandler(this.afterActionDelay_Tick);
            // 
            // autoSweep
            // 
            this.autoSweep.Interval = 2500;
            this.autoSweep.Tick += new System.EventHandler(this.autoSweep_Tick);
            // 
            // Main
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(751, 527);
            this.Controls.Add(this.splitContainer1);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "Main";
            this.Opacity = 0.99D;
            this.ShowIcon = false;
            this.ShowInTaskbar = false;
            this.TopMost = true;
            this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Main_FormClosing);
            this.Load += new System.EventHandler(this.Main_Load);
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
            this.splitContainer1.ResumeLayout(false);
            this.ResumeLayout(false);

        } 
        #endregion

        #region Keyboard and Mouse events
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            if (keyData == Keys.Escape)
            {
                this.Close();
                return true;
            } else if (keyData == Keys.F12)
            {
                ResetFourSteps();
                disableClickCapture = true;
                autoSweep.Enabled = false;

                //Show the options panel
                Cursor.Show();
                OptionsMenu options = new OptionsMenu(this);
                if (options.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    //Set the values and start/stop the timer
                    zoomQuantity = (int)options.numericUpDown1.Value;
                    autoSweep.Interval = (int)(options.numericUpDown2.Value * 1000);
                    autoSweep.Enabled = options.checkBox1.Checked;
                }
                Cursor.Hide();
                disableClickCapture = false;
                return true;
            }
            else
            {
                return base.ProcessCmdKey(ref msg, keyData);
            }
        }

        private void ResetFourSteps()
        {
            this.Hide();
            this.Opacity = 0;
            screenUpdater.Enabled = false;
            currentStage = FourStepStage.NOT_INITIATED;
            primaryScreen = Screen.PrimaryScreen;
            zoomIndex = 0;
            zoomCuadrants = new int[zoomQuantity];
        }

        public void MouseActivity(object sender, MouseEventArgs e)
        {
            if ((e.Button.ToString().CompareTo("Left") == 0) && (disableClickCapture == false))
            {
                if (onFirstTimeRun == true)
                {
                    onFirstTimeRun = false;
                    if (firstTimeRunDialog != null)
                    {
                        firstTimeRunDialog.Close();
                    }
                    this.Focus();
                }
                if (currentStage == FourStepStage.NOT_INITIATED)
                {
                    currentStage = FourStepStage.ON_SCAN;
                    this.Show();
                    screenUpdater.Enabled = true;                    
                    TakeScreenshot();
                } else if (currentStage == FourStepStage.ON_SCAN)
                {
                    if (zoomIndex < zoomQuantity)
                    {
                        if (lastBrightedCuadrant > 0 && lastBrightedCuadrant < 5)
                        {
                            zoomCuadrants[zoomIndex] = lastBrightedCuadrant;
                            zoomIndex++;
                            TakeScreenshot();
                        }
                    } else
                    {
                        //This happens when the zoom is over
                        currentStage = FourStepStage.ON_ACTION_SELECTION;
                        
                        //Draw the action form                        
                        //Bitmap optionFormBitmap = new Bitmap(primaryScreen.Bounds.Width, primaryScreen.Bounds.Height);
                        //using (Graphics gr = Graphics.FromImage(optionFormBitmap))
                        //{
                        //    gr.DrawImage(new Bitmap(Resources.clickform), primaryScreen.Bounds);
                        //    gr.DrawImage(splitContainer1.Panel1.BackgroundImage, new Rectangle((primaryScreen.Bounds.Width / 4), (primaryScreen.Bounds.Height / 4), primaryScreen.Bounds.Width / 2, primaryScreen.Bounds.Height / 2));
                        //}
                        //splitContainer1.Panel1.BackgroundImage = optionFormBitmap;

                        screenPoint = new Bitmap(splitContainer1.Panel1.BackgroundImage, primaryScreen.Bounds.Width / 2, primaryScreen.Bounds.Height / 2);
                    }
                } else if (currentStage == FourStepStage.ON_ACTION_SELECTION)
                {
                    disableClickCapture = true;
                    actionToPerform = currentCuadrantPosition;
                    afterActionDelay.Start();                    
                }
            }            
        }

        private void afterActionDelay_Tick(object sender, EventArgs e)
        {
            afterActionDelay.Enabled = false;
            afterActionDelay.Stop();
            this.Hide();
            currentStage = FourStepStage.NOT_INITIATED;

            Point newCursorPosition = new Point(primaryScreen.Bounds.Width / 2, primaryScreen.Bounds.Height / 2);
            Size zoomAddition = new Size(primaryScreen.Bounds.Width / 2, primaryScreen.Bounds.Height / 2);

            for (int index = 0; index < zoomIndex; index++)
            {
                zoomAddition = new Size(zoomAddition.Width / 2, zoomAddition.Height / 2);
                if (zoomCuadrants[index] == 1)
                {
                    newCursorPosition = new Point(newCursorPosition.X - zoomAddition.Width, newCursorPosition.Y - zoomAddition.Height);
                }
                else if (zoomCuadrants[index] == 2)
                {
                    newCursorPosition = new Point(newCursorPosition.X + zoomAddition.Width, newCursorPosition.Y - zoomAddition.Height);
                }
                else if (zoomCuadrants[index] == 3)
                {
                    newCursorPosition = new Point(newCursorPosition.X - zoomAddition.Width, newCursorPosition.Y + zoomAddition.Height);
                }
                else if (zoomCuadrants[index] == 4)
                {
                    newCursorPosition = new Point(newCursorPosition.X + zoomAddition.Width, newCursorPosition.Y + zoomAddition.Height);
                }
            }                        
            ClearZoomStack();
            
            //Process last selected cuadrant (a fix is needed here, this code is not very optimal)
            zoomAddition = new Size(zoomAddition.Width / 2, zoomAddition.Height / 2);
            if (lastBrightedCuadrant == 1)
            {
                newCursorPosition = new Point(newCursorPosition.X - zoomAddition.Width, newCursorPosition.Y - zoomAddition.Height);
            } else if (lastBrightedCuadrant == 2)
            {
                newCursorPosition = new Point(newCursorPosition.X + zoomAddition.Width, newCursorPosition.Y - zoomAddition.Height);
            } else if (lastBrightedCuadrant == 3)
            {
                newCursorPosition = new Point(newCursorPosition.X - zoomAddition.Width, newCursorPosition.Y + zoomAddition.Height);
            } else if (lastBrightedCuadrant == 4)
            {
                newCursorPosition = new Point(newCursorPosition.X + zoomAddition.Width, newCursorPosition.Y + zoomAddition.Height);
            }

            Cursor.Position = newCursorPosition;
            if (actionToPerform == 1)
            {
                mouse_event((int)(MouseEventFlags.LEFTDOWN | MouseEventFlags.LEFTUP), Cursor.Position.X, Cursor.Position.Y, 0, 0);
                //mouse_event((int)(MouseEventFlags.LEFTDOWN), Cursor.Position.X, Cursor.Position.Y, 0, 0);
                //mouse_event((int)(MouseEventFlags.LEFTUP), Cursor.Position.X, Cursor.Position.Y, 0, 0);
            }
            else if (actionToPerform == 2)
            {
                mouse_event((int)(MouseEventFlags.LEFTDOWN | MouseEventFlags.LEFTUP), Cursor.Position.X, Cursor.Position.Y, 0, 0);
                Thread.Sleep(100);
                mouse_event((int)(MouseEventFlags.LEFTDOWN | MouseEventFlags.LEFTUP), Cursor.Position.X, Cursor.Position.Y, 0, 0);
            }
            else if (actionToPerform == 3)
            {
                mouse_event((int)(MouseEventFlags.RIGHTDOWN | MouseEventFlags.RIGHTUP), Cursor.Position.X, Cursor.Position.Y, 0, 0);
            }
            else if (actionToPerform == 4)
            {
                //Nothing to do
            }

            disableClickCapture = false;
        }
        #endregion

        #region LoadAndClosingEvents
        private void Main_Load(object sender, EventArgs e)
        {
            this.Opacity = 0;
            screenUpdater.Enabled = false;
            currentStage = FourStepStage.NOT_INITIATED;
            primaryScreen = Screen.PrimaryScreen;
            zoomIndex = 0;
            zoomCuadrants = new int[zoomQuantity];

            actHook = new UserActivityHook(true, true); //Create an instance with global hooks
            actHook.OnMouseActivity += new MouseEventHandler(MouseActivity); //Currently there is no mouse activity
            actHook.Start();

            this.DoubleBuffered = true;
            this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
            this.UpdateStyles();

            firstTimeRunDialog = new FirstTimeRun();
            firstTimeRunDialog.Show();

            cursorImage = Resources.cursor;
            Cursor.Hide();

            //Uncomment to make clickable trough
            //int initialStyle = GetWindowLong(this.Handle, -20);
            //SetWindowLong(this.Handle, -20, initialStyle | 0x80000 | 0x20);

            //TakeScreenshot(); //Uncomment to take screeshot when program starts
        }

        private void Main_FormClosing(object sender, FormClosingEventArgs e)
        {
            Cursor.Show();
            screenUpdater.Enabled = false;
            actHook.Stop();
        }
        #endregion

        public void ClearZoomStack()
        {
            zoomIndex = 0;
            for (int count = 0; count < zoomQuantity; count++)
            {
                zoomCuadrants[count] = 0;
            }
        }

        #region BrightingFunctions
        public void BrightCuadrant(int cuadrant, bool drawCursor)
        {
            if (dimmedScreen == null)
            {
                return;
            }

            if (cuadrant > 0 && cuadrant < 5)
            {
                filter.AdjustValue = +50;
                Bitmap brightPart = (Bitmap)dimmedScreen.Clone();
                Rectangle cursorCuadrant = new Rectangle();
                if (cuadrant == 1)
                {
                    filter.ApplyInPlace(brightPart, firstCuadrant);
                    cursorCuadrant = firstCuadrant;
                }
                else if (cuadrant == 2)
                {
                    filter.ApplyInPlace(brightPart, secondCuadrant);
                    cursorCuadrant = secondCuadrant;
                }
                else if (cuadrant == 3)
                {
                    filter.ApplyInPlace(brightPart, thirdCuadrant);
                    cursorCuadrant = thirdCuadrant;
                }
                else if (cuadrant == 4)
                {
                    filter.ApplyInPlace(brightPart, fourthCuadrant);
                    cursorCuadrant = fourthCuadrant;
                }
                lastBrightedCuadrant = cuadrant;

                if (drawCursor)
                {
                    using (Graphics gr = Graphics.FromImage(brightPart))
                    {                        
                        gr.DrawImage(cursorImage, ((cursorCuadrant.Right - cursorCuadrant.Left) / 2) + cursorCuadrant.X, ((cursorCuadrant.Bottom - cursorCuadrant.Top) / 2) + cursorCuadrant.Y);
                    }
                }

                splitContainer1.Panel1.BackgroundImage = brightPart;
            }
        }

        private void screenUpdater_Tick(object sender, EventArgs e)
        {
            if (Cursor.Position.X > cuadrantSize.Width)
            {
                if (Cursor.Position.Y > cuadrantSize.Height)
                {
                    currentCuadrantPosition = 4;
                }
                else
                {
                    currentCuadrantPosition = 2;
                }
            }
            else
            {
                if (Cursor.Position.Y > cuadrantSize.Height)
                {
                    currentCuadrantPosition = 3;
                }
                else
                {
                    currentCuadrantPosition = 1;
                }
            }
            if (currentStage == FourStepStage.ON_SCAN)
            {
                if (lastBrightedCuadrant != currentCuadrantPosition)
                {
                    BrightCuadrant(currentCuadrantPosition, true);
                }
            } else if (currentStage == FourStepStage.ON_ACTION_SELECTION)
            {
                if (lastBrightedAction != currentCuadrantPosition)
                {
                    Bitmap optionFormBitmap = new Bitmap(primaryScreen.Bounds.Width, primaryScreen.Bounds.Height);
                    using (Graphics gr = Graphics.FromImage(optionFormBitmap))
                    {
                        Bitmap actionBitmap;

                        //For sure there is a way to optimize this, ideas?
                        if (currentCuadrantPosition == 1)
                        {
                            actionBitmap = Resources.action1;
                        }
                        else if (currentCuadrantPosition == 2)
                        {
                            actionBitmap = Resources.action2;
                        }
                        else if (currentCuadrantPosition == 3)
                        {
                            actionBitmap = Resources.action3;
                        }
                        else if (currentCuadrantPosition == 4)
                        {
                            actionBitmap = Resources.action4;
                        }
                        else
                        {
                            actionBitmap = Resources.action1;
                        }

                        gr.DrawImage(actionBitmap, primaryScreen.Bounds);
                        gr.DrawImage(screenPoint, new Rectangle((primaryScreen.Bounds.Width / 4), (primaryScreen.Bounds.Height / 4), primaryScreen.Bounds.Width / 2, primaryScreen.Bounds.Height / 2));
                    }
                    splitContainer1.Panel1.BackgroundImage = optionFormBitmap;
                    lastBrightedAction = currentCuadrantPosition;
                }
            }
        }
        #endregion

        public void TakeScreenshot()
        {
            using (var bitmap = new Bitmap(primaryScreen.Bounds.Width, primaryScreen.Bounds.Height))
            {
                using (var graphics = Graphics.FromImage(bitmap))
                {
                    //this.Hide();
                    this.Opacity = 0;
                    graphics.CopyFromScreen(new Point(primaryScreen.Bounds.Left, primaryScreen.Bounds.Top), new Point(0, 0), primaryScreen.Bounds.Size);
                    this.Opacity = 99;
                    //this.Show();
                    filter.AdjustValue = -50;
                    filter.ApplyInPlace(bitmap);
                    dimmedScreen = (Bitmap)bitmap.Clone();                    
                }
            }

            Rectangle regionToClone = primaryScreen.Bounds;

            for (int index = 0; index < zoomIndex; index++)
            {
                Size cuadrantSize = new Size(regionToClone.Width / 2, regionToClone.Height / 2);

                if (zoomCuadrants[index] == 1)
                {
                    //No changes needed
                }
                else if (zoomCuadrants[index] == 2)
                {
                    regionToClone.X += cuadrantSize.Width;
                }
                else if (zoomCuadrants[index] == 3)
                {
                    regionToClone.Y += cuadrantSize.Height;
                }
                else if (zoomCuadrants[index] == 4)
                {
                    regionToClone.X += cuadrantSize.Width;
                    regionToClone.Y += cuadrantSize.Height;
                }
                regionToClone.Size = new Size(regionToClone.Size.Width / 2, regionToClone.Size.Height / 2);
            }

            dimmedScreen = dimmedScreen.Clone(regionToClone, dimmedScreen.PixelFormat);

            Bitmap newImage = new Bitmap(primaryScreen.Bounds.Width, primaryScreen.Bounds.Height);
            using (Graphics gr = Graphics.FromImage(newImage))
            {
                gr.SmoothingMode = SmoothingMode.HighQuality;
                gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
                gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
                gr.DrawImage(dimmedScreen, new Rectangle(0, 0, newImage.Width, newImage.Height));
                gr.DrawString("Press F12 to see the options menu. Press ESC to exit program.", this.Font, Brushes.Black, new PointF(0, 0));

            }
            dimmedScreen = (Bitmap)newImage.Clone();
            BrightCuadrant(lastBrightedCuadrant, true);
            //currentStage = FourStepStage.ON_SCAN;
        }

        private void autoSweep_Tick(object sender, EventArgs e)
        {
            autoSweepCuadrant++;
            if (autoSweepCuadrant > 4)
            {
                autoSweepCuadrant = 1;
            }

            Point newCursorPosition = new Point(0, 0);

            if (autoSweepCuadrant == 1)
            {
                newCursorPosition = new Point((primaryScreen.Bounds.Width / 2) - (primaryScreen.Bounds.Width / 4), (primaryScreen.Bounds.Height / 2) - (primaryScreen.Bounds.Height / 4));
            }
            else if (autoSweepCuadrant == 2)
            {
                newCursorPosition = new Point((primaryScreen.Bounds.Width / 2) + (primaryScreen.Bounds.Width / 4), (primaryScreen.Bounds.Height / 2) - (primaryScreen.Bounds.Height / 4));
            }
            else if (autoSweepCuadrant == 3)
            {
                newCursorPosition = new Point((primaryScreen.Bounds.Width / 2) - (primaryScreen.Bounds.Width / 4), (primaryScreen.Bounds.Height / 2) + (primaryScreen.Bounds.Height / 4));
            }
            else if (autoSweepCuadrant == 4)
            {
                newCursorPosition = new Point((primaryScreen.Bounds.Width / 2) + (primaryScreen.Bounds.Width / 4), (primaryScreen.Bounds.Height / 2) + (primaryScreen.Bounds.Height / 4));
            }
            Cursor.Position = newCursorPosition;
        }

    }
}
